package com.ssm.changeunit.command;

import com.ssm.changeunit.*;
import com.ssm.changeunit.persistance.ManagedItemListDAO;
import com.ssm.changeunit.persistance.ManagedItemListDTO;
import com.ssm.changeunit.persistance.PersistanceException;
import com.ssm.changeunit.util.FileUtil;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import com.ssm.changeunit.Environment;
import com.ssm.changeunit.persistance.ChangeUnitBranchDAO;
import com.ssm.changeunit.persistance.ChangeUnitDAO;
import com.ssm.changeunit.persistance.ChangeUnitDTO;
import com.ssm.changeunit.persistance.ChildDAO;
import com.ssm.changeunit.persistance.DatabaseManager;
import com.ssm.changeunit.persistance.ManagedChangeUnitDAO;
import com.ssm.changeunit.persistance.ManagedChangeUnitDTO;
import com.ssm.changeunit.persistance.ParentDAO;
import org.apache.log4j.Logger;

/**
 * 
 * @author 시리우스
 */
public class CommitCommandWorker extends CommandWorker {
    private Logger log = Logger.getLogger(CommitCommandWorker.class);
            
    public CommitCommandWorker () {
    }

    public void work (CommandWorkerOptions commandWorkerOptions) throws CommandWorkerException {
        if( commandWorkerOptions == null )
            throw new CommandWorkerException("Option이 null입니다.");
        
        if( !commandWorkerOptions.hasCommandWorkerOption("log") )
            throw new CommandWorkerException("Log Option이 존재하지 않습니다.");
        
        // Item option을 꺼내옵니다.
        CommandWorkerOption itemCommandWorkerOption = commandWorkerOptions.getCommandWorkerOption("log");
        
        // Item option에 해당하는 item들을 가져옵니다.
        String[] items = itemCommandWorkerOption.getValues();
        
        // item의 값이 없는 경우
        if( items.length == 0 )
            throw new CommandWorkerException("Log의 값이 존재하지 않습니다.");
        
        // 데이터베이스 체크
        log.debug("데이터베이스가 존재하는지 검사합니다.");
        
        try {
            if(!DatabaseManager.getInstance().isExists())
                throw new CommandWorkerException("데이터베이스가 존재하지 않습니다.");
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
        
        // 현재 Branch를 찾아옵니다.
        Branch currentBranch = null;
        
        try {
            currentBranch = new BranchFactory().createProduct(Environment.getCurrentBranch());
        }
        catch(IOException ex) {
            throw new CommandWorkerException("Branch를 가져오는 도중 에러가 발생했습니다.", ex);
        }
        catch(CreateException ex) {
            throw new CommandWorkerException("Branch를 가져오는 도중 에러가 발생했습니다.", ex);
        }
        
        log.debug("현재 Branch: " + currentBranch.getBranchName());
        
        // 기존 ManagedItemList를 가져온다.
        ManagedChangeUnitDAO managedChangeUnitDAO = new ManagedChangeUnitDAO();

        ManagedChangeUnitDTO managedChangeUnitDTO = null;
        
        try {
            managedChangeUnitDTO = managedChangeUnitDAO.find();
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }

        log.debug("ManagedChangeUnit: " + managedChangeUnitDTO);

        ChangeUnit currentChangeUnit = null;

        try {
            currentChangeUnit = currentBranch.getCurrentChangeUnit();
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }

        // Branch의 최신 ChangeUnit과 ManagedChangeUnit이 다른 경우
        // Update가 필요하다는 메시지를 출력한다.
        if(!managedChangeUnitDTO.getManagedChangeUnitId().equals(currentChangeUnit.getChangeUnitId()))  {
            log.debug("ManagedChangeUnitId: " + managedChangeUnitDTO.getManagedChangeUnitId());
            log.debug("CurrentChanageUnitId: " + currentChangeUnit.getChangeUnitId());
            
            log.warn("Update가 필요합니다.");

            return ;
        }

        try {
            // Commit 로직 시작
            // 새로운 ChangeUnit 생성
            ChangeUnit newChangeUnit = null;
            
            if(currentChangeUnit.getChangeUnitId().equals("0000-0000-0000")) {
                newChangeUnit = new ChangeUnitFactory().createProduct(UUID.randomUUID().toString());
                
                ChangeUnit firstParent = currentChangeUnit.getParents().get(0);
                ChangeUnit secondParent = currentChangeUnit.getParents().get(1);
                
                log.debug("첫번째 부모 로그!: " + firstParent.getLog());
                log.debug("두번째 부모 로그!: " + secondParent.getLog());
                
                newChangeUnit.addParent(firstParent);
                newChangeUnit.addParent(secondParent);
                
                newChangeUnit.addBranch(currentBranch);
                
                ChangeUnitDAO changeUnitDAO = new ChangeUnitDAO();                
                ChangeUnitDTO changeUnitDTO = changeUnitDAO.find(currentChangeUnit.getChangeUnitId());                
                changeUnitDAO.delete(changeUnitDTO.getChangeUnitId());
                
                ChangeUnitBranchDAO changeUnitBranchDAO = new ChangeUnitBranchDAO();
                changeUnitBranchDAO.deleteByChangeUnitId("0000-0000-0000");
                
                ParentDAO parentDAO = new ParentDAO();
                parentDAO.deleteByChangeUnitId("0000-0000-0000");
                
                ChildDAO childDAO = new ChildDAO();
                childDAO.deleteByChangeUnitId("0000-0000-0000");
            }
            else {
                newChangeUnit = new ChangeUnitFactory().createProduct(UUID.randomUUID().toString());
                
                // 자식 설정
                currentChangeUnit.addChild(newChangeUnit);

                // 부모 설정
                newChangeUnit.addParent(currentChangeUnit);
                
                // Branch 추가
                newChangeUnit.addBranch(currentBranch);
            }
            
            // 유저 ID Setting
            newChangeUnit.setUserId(Environment.USER_ID);
            
            // 로그 Setting
            newChangeUnit.setLog(items[0]);
            
            // Branch의 최신 값으로 ChnageUnit 설정
            currentBranch.setCurrentChangeUnit(newChangeUnit);
            
            // ChangeUnitItem 추가
            // Commit할 대상들을 가져온다.
            List<ManagedItemListDTO> managedItemListDTOs = new ManagedItemListDAO().list();
            
            for(ManagedItemListDTO managedItemListDTO : managedItemListDTOs) {
                // 추가할 FilePath를 구한다.
                String newFilePath = managedItemListDTO.getFilePath();
                String newFileFullPath = Environment.ROOT_DIR + newFilePath;
                
                // 새로운 파일의 내용을 가져옵니다.
                String newContent = "";
                
                try {
                    newContent = FileUtil.readFromFile(newFileFullPath);
                }
                catch(IOException ex) {
                    throw new CommandWorkerException(ex);
                }
                
                // 새로운 ChangeUnitItem 생성.
                ChangeUnitItem newChangeUnitItem = new ChangeUnitItemFactory().createProduct(UUID.randomUUID().toString());                
                newChangeUnitItem.setChangeUnit(newChangeUnit);
                newChangeUnitItem.setFilePath(newFilePath);
                
                // 전 ChangeUnitItem을 찾습니다.
                ChangeUnitItem prevChangeUnitItem = null;
                
                // 전 Commit의 Item들을 가져온다.
                List<ChangeUnit> parents = newChangeUnit.getParents();
                
                for(ChangeUnit parent : parents) {
                    List<ChangeUnitItem> prevChangeUnitItems = parent.getChangeUnitItems();

                    // Commit될 FilePath가 전 ChangeUnit에 있는지 검사한다.
                    for(ChangeUnitItem changeUnitItem : prevChangeUnitItems) {
                        log.debug(newFilePath + " == " + changeUnitItem.getFilePath());

                        // 찾았다..
                        if(changeUnitItem.getFilePath().equals(newFilePath)) {
                            prevChangeUnitItem = changeUnitItem;

                            break;
                        }
                    }
                    
                    if(prevChangeUnitItem != null)
                        break;
                }
                
                // 찾은 경우
                // 2개를 비교해서 값이 다를 경우에만 
                // 새로운 ChangeLog를 생성한다.
                if( prevChangeUnitItem != null ) {
                    log.debug("2개를 비교합니다..");
                    
                    String prevContent = new String(prevChangeUnitItem.getRefChangeLog().getContent());

                    log.debug("PrevContent: " + prevContent);
                    log.debug("NewContent: " + newContent);
                    
                    // 전 값과 같다면.. 새로운 ChangeLog를 생성하지 않아도 된다.
                    if(prevContent.equals(newContent)) {
                        log.debug("전값과 동일한 Content를 가지고 있습니다.");
                        
                        // 전거와 동일한 ChangeLog를 참조하게 한다.
                        newChangeUnitItem.setRefChangeLog(prevChangeUnitItem.getRefChangeLog());
                    }
                    else {
                        log.debug("전값과 동일하지 않은 Content를 가지고 있습니다.");
                        
                        // 새로운 ChangeLog 생성
                        ChangeLog newChangeLog = new ChangeLogFactory().createProduct(UUID.randomUUID().toString());
                        newChangeLog.setFilePath(newFilePath);
                        newChangeLog.setContent(newContent.getBytes());
                        
                        // 새로운 ChangeLog를 참조하게 함
                        newChangeUnitItem.setRefChangeLog(newChangeLog);
                    }
                    
                    // PrevChangeUnitItem 연결
                    newChangeUnitItem.setFirstParentChangeUnitItem(prevChangeUnitItem);
                }
                // 못찾은 경우
                else {
                    ChangeLog newChangeLog = new ChangeLogFactory().createProduct(UUID.randomUUID().toString());
                    newChangeLog.setFilePath(newFilePath);
                    newChangeLog.setContent(newContent.getBytes());

                    // 새로운 ChangeLog를 참조하게 함
                    newChangeUnitItem.setRefChangeLog(newChangeLog);
                }
                
                // ChangeUnit에 연결
                newChangeUnit.addChangeUnitItem(newChangeUnitItem);
            }
            
            // ManagedChangeUnit 삽입
            managedChangeUnitDTO.setManagedChangeUnitId(newChangeUnit.getChangeUnitId());

            managedChangeUnitDAO.update(managedChangeUnitDTO);
        }
        catch(CreateException ex) {
            throw new CommandWorkerException(ex);
        }
        catch(PersistanceException ex) {
            throw new CommandWorkerException(ex);
        }
    }
}